Expand description
§Tiny OBJ Loader
A tiny OBJ loader, inspired by Syoyo’s excellent tinyobjloader
.
Aims to be a simple and lightweight option for loading OBJ
files.
Just returns two Vec
s containing loaded models and materials.
§Triangulation
Meshes can be triangulated on the fly or left as-is.
Only polygons that are trivially convertible to triangle fans are supported. Arbitrary polygons may not behave as expected. The best solution would be to convert your mesh to solely consist of triangles in your modeling software.
§Optional – Normals & Texture Coordinates
It is assumed that all meshes will at least have positions, but normals and texture coordinates are optional.
If no normals or texture coordinates are found then the corresponding
Vec
s for the Mesh
will be empty.
§Flat Data
Values are stored packed as f32
s (or f64
s with the use_f64 feature)
in flat Vec
s.
For example, the positions
member of a Mesh
will contain [x, y, z, x, y, z, ...]
which you can then use however you like.
§Indices
Indices are also loaded and may re-use vertices already existing in the
mesh, this data is stored in the indices
member.
When a Mesh
contains per vertex per face normals or texture coordinates,
positions can be duplicated to be per vertex per face too via the
single_index
flag. This potentially changes
the topology (faces may become disconnected even though their vertices still
share a position in space).
By default separate indices for normals and texture coordinates are created.
This also guarantees that the topology of the Mesh
does not change when
either of the latter are specified per vertex per face.
§Materials
Standard MTL
attributes are supported too. Any unrecognized parameters
will be stored in a HashMap
containing the key-value pairs of the
unrecognized parameter and its value.
§Example
In this simple example we load the classic Cornell Box model that only
defines positions and print out its attributes. This example is a slightly
trimmed down version of print_model_info
and print_material_info
combined together, see them for a version that also prints out normals and
texture coordinates if the model has them.
The LoadOptions
used are typical for the case when the mesh is going to
be sent to a realtime rendering context (game engine, GPU etc.).
use tobj;
let cornell_box = tobj::load_obj("obj/cornell_box.obj", &tobj::GPU_LOAD_OPTIONS);
assert!(cornell_box.is_ok());
let (models, materials) = cornell_box.expect("Failed to load OBJ file");
// Materials might report a separate loading error if the MTL file wasn't found.
// If you don't need the materials, you can generate a default here and use that
// instead.
let materials = materials.expect("Failed to load MTL file");
println!("# of models: {}", models.len());
println!("# of materials: {}", materials.len());
for (i, m) in models.iter().enumerate() {
let mesh = &m.mesh;
println!("model[{}].name = \'{}\'", i, m.name);
println!("model[{}].mesh.material_id = {:?}", i, mesh.material_id);
println!(
"Size of model[{}].face_arities: {}",
i,
mesh.face_arities.len()
);
let mut next_face = 0;
for f in 0..mesh.face_arities.len() {
let end = next_face + mesh.face_arities[f] as usize;
let face_indices: Vec<_> = mesh.indices[next_face..end].iter().collect();
println!(" face[{}] = {:?}", f, face_indices);
next_face = end;
}
// Normals and texture coordinates are also loaded, but not printed in this example
println!("model[{}].vertices: {}", i, mesh.positions.len() / 3);
assert!(mesh.positions.len() % 3 == 0);
for v in 0..mesh.positions.len() / 3 {
println!(
" v[{}] = ({}, {}, {})",
v,
mesh.positions[3 * v],
mesh.positions[3 * v + 1],
mesh.positions[3 * v + 2]
);
}
}
for (i, m) in materials.iter().enumerate() {
println!("material[{}].name = \'{}\'", i, m.name);
if let Some(ambient) = m.ambient {
println!(
" material.Ka = ({}, {}, {})",
ambient[0], ambient[1], ambient[2]
);
}
if let Some(diffuse) = m.diffuse {
println!(
" material.Kd = ({}, {}, {})",
diffuse[0], diffuse[1], diffuse[2]
);
}
if let Some(specular) = m.specular {
println!(
" material.Ks = ({}, {}, {})",
specular[0], specular[1], specular[2]
);
}
if let Some(shininess) = m.shininess {
println!(" material.Ns = {}", shininess);
}
if let Some(dissolve) = m.dissolve {
println!(" material.d = {}", dissolve);
}
if let Some(ambient_texture) = &m.ambient_texture {
println!(" material.map_Ka = {}", ambient_texture);
}
if let Some(diffuse_texture) = &m.diffuse_texture {
println!(" material.map_Kd = {}", diffuse_texture);
}
if let Some(specular_texture) = &m.specular_texture {
println!(" material.map_Ks = {}", specular_texture);
}
if let Some(shininess_texture) = &m.shininess_texture {
println!(" material.map_Ns = {}", shininess_texture);
}
if let Some(normal_texture) = &m.normal_texture {
println!(" material.map_Bump = {}", normal_texture);
}
if let Some(dissolve_texture) = &m.dissolve_texture {
println!(" material.map_d = {}", dissolve_texture);
}
for (k, v) in &m.unknown_param {
println!(" material.{} = {}", k, v);
}
}
§Rendering Examples
For an example of integration with glium
to make a simple OBJ viewer, check out tobj viewer
.
Some more sample images can be found in this gallery.
The Rungholt model shown below is reasonably large (6.7M triangles, 12.3M vertices) and is loaded in ~7.47s using a peak of ~1.1GB of memory on a Windows 10 machine with an i7-4790k and 16GB of 1600Mhz DDR3 RAM with tobj 0.1.1 on rustc 1.6.0. The model can be found on Morgan McGuire’s meshes page and was originally built by kescha. Future work will focus on improving performance and memory usage.
For an example of integration within a ray tracer, check out tray_rust’s mesh module. The Stanford Buddha and Dragon from the Stanford 3D Scanning Repository both load quite quickly. The Rust logo model was made by Nylithius on BlenderArtists. The materials used are from the MERL BRDF Database.
§Features
-
ahash
– On by default. UseAHashMap
for hashing when reading files and merging vertices. To disable and use the slowerHashMap
instead, unset default features inCargo.toml
:[dependencies.tobj] default-features = false
-
merging
– Adds support for merging identical vertex positions on disconnected faces during import.Warning: this feature uses const generics and thus requires at least a
beta
toolchain to build. -
reordering
– Adds support for reordering the normal- and texture coordinate indices. -
async
– Adds support for async loading of obj files from a buffer, with an async material loader. Useful in environments that do not support blocking IO (e.g. WebAssembly). -
[‘use_f64’] - Uses double-precision (f64) instead of single-precision (f32) floating point types
Structs§
- Options for processing the mesh during loading.
- A material that may be referenced by one or more
Mesh
es. - A mesh made up of triangles loaded from some
OBJ
file. - A named model within the file.
Enums§
- Possible errors that may occur while loading
OBJ
andMTL
files.
Constants§
- Typical
LoadOptions
for using meshes in a GPU/relatime context. - Typical
LoadOptions
for using meshes with an offline rendeder.
Functions§
- Load the materials defined in a
MTL
file. - Load the various materials in a
MTL
buffer. - Load the various objects specified in the
OBJ
file and any associatedMTL
file. - Load the various meshes in an
OBJ
buffer. - Load the various meshes in an
OBJ
buffer.